package com.learning.optimize.jdk.reflect;

import lombok.Getter;
import lombok.Setter;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;

/**
 * ClassName: FieldReflect
 * Description: Field 类以及属性反射
 * Date: 2018/7/18 14:15 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
public class FieldReflect {

    public static void main(String[] args) throws Exception {
//        test();
        test1();
//        test3();
    }

    /**
     * Class 获取Field 相关操作
     *
     * @throws Exception
     */
    static void test() throws Exception {
        Class<?> clazz = Class.forName("com.learning.optimize.jdk.reflect.Student");
        // 获取指定字段名称的Field类,注意字段修饰符必须为public而且存在该字段,
        // 否则抛NoSuchFieldException
        Field field = clazz.getField("age");
        System.out.println("field:" + field);

        // 获取所有修饰符为 public的字段, 包含父类字段,注意修饰符为 public 才会获取
        Field[] fields = clazz.getFields();
        for (Field f : fields) {
            System.out.println("f:" + f.getDeclaringClass());
        }

        System.out.println("================getDeclaredFields====================");
        // 获取当前类所字段(包含private字段),注意不包含父类的字段
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field f : fields2) {
            System.out.println("f2:" + f.getDeclaringClass());
        }

        // 获取指定字段名称的Field类,可以是任意修饰符的自动,注意不包含父类的字段
        Field field2 = clazz.getDeclaredField("desc");
        System.out.println("field2:" + field2);

/*
**
field:public int com.learning.optimize.jdk.reflect.Person.age
f:class com.learning.optimize.jdk.reflect.Student
f:class com.learning.optimize.jdk.reflect.Person
f:class com.learning.optimize.jdk.reflect.Person
================getDeclaredFields====================
f2:class com.learning.optimize.jdk.reflect.Student
f2:class com.learning.optimize.jdk.reflect.Student
field2:public java.lang.String com.learning.optimize.jdk.reflect.Student.desc
* */
    }

    /**
     * Field 获取设置的api
     * 1、获取父类的Public属性
     * 2、获取自己的私有private属性
     *
     * @throws Exception
     */
    static void test1() throws Exception {
        //获取Class对象引用
        Class<?> clazz = Class.forName("com.learning.optimize.jdk.reflect.Student");

        Student student = (Student) clazz.newInstance();
        //获取父类 public 字段并赋值
        Field ageField = clazz.getField("age");
        ageField.set(student, 18);
        Field nameField = clazz.getField("name");
        nameField.set(student, "Lily");

        System.out.println(student);

        //只获取当前类的字段（包括private）,不获取父类的字段
        Field descField = clazz.getDeclaredField("desc");
        System.out.println(descField.getGenericType());

        descField.set(student, "I am student");
        Field scoreField = clazz.getDeclaredField("score");
        System.out.println(scoreField.getType());
        //设置可访问，score是private的
        scoreField.setAccessible(true);
        scoreField.set(student, 88);
        System.out.println(student);

        //获取字段值
        System.out.println(scoreField.get(student));

        /*Student{age=18, name='Lily', desc='null', score=0}
        class java.lang.String
        int
        Student{age=18, name='Lily', desc='I am student', score=88}
        88*/
    }

    /**
     * 两种属性的 set| get 方法
     */
    static void test2() throws Exception {
        Class<?> clazz = Class.forName("com.learning.optimize.jdk.reflect.Student");
        Student student = (Student) clazz.newInstance();

        // 使用 Set 方法设置
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            Class<?> type = declaredField.getType();
            if (type == String.class) {
                declaredField.set(student, "sam");
            }

            if (type == int.class || type == Integer.class) {
                declaredField.setInt(student, 30);
            }
        }
        System.out.println(student);

        // 使用 PropertyDescriptor 设置
        for (Field declaredField : declaredFields) {
            String name = declaredField.getName();
            Class<?> type = declaredField.getType();
            Object paramVal = null;
            if (type == String.class) {
                paramVal = "eason";
            }
            if (type == int.class || type == Integer.class) {
                paramVal = 2;
            }
            // 利用 PropertyDescriptor 获取set、get方法
            PropertyDescriptor pd = new PropertyDescriptor(name, clazz);
            //调用set方法。Write为set，read为get
            pd.getWriteMethod().invoke(student, paramVal);
        }
        System.out.println(student);

/*
输出：
Student{age=0, name='null', desc='sam', score=30}
Student{age=0, name='null', desc='eason', score=2}
* */
    }

}

@Setter
@Getter
class Person {
    public int age;
    public String name;

}

@Setter
@Getter
class Student extends Person {
    public String desc;
    private int score;

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("Student{");
        sb.append("age=").append(age);
        sb.append(", name='").append(name).append('\'');
        sb.append(", desc='").append(desc).append('\'');
        sb.append(", score=").append(score);
        sb.append('}');
        return sb.toString();
    }
}
